-----------------------------------------------------------------------------
--
-- Module      :  Data.Units.Class
-- Copyright   :
-- License     :  AllRightsReserved
--
-- Maintainer  :  eduard.sergeev@gmail.com
-- Stability   :
-- Portability :
--
-- QNum and QFractional ('quantitified' Num and Fractional from Prelude) classes definitions
--
-----------------------------------------------------------------------------

{-# LANGUAGE RebindableSyntax, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts,
UndecidableInstances  #-}

module Data.Units.Class (

   QNum(..),
   QFractional(..),
   QReal(..),
   QIntegral(..),
   QFloating(..),

   (^),
   --even,odd,
   --gcd,

   QApplicative(..),

) where


import qualified Prelude as P
import Data.Eq
import Data.Bool
import Data.Function

import Data.TypeLevel.Class
import Data.TypeLevel.Boolean
import Data.Units.Types


-- * QNum typeclass - quantitified "Num"


class QNum q n where
    toNum :: q n u -> n
    (+)     :: q n u -> q n u -> q n u
    (-)     :: q n u -> q n u -> q n u
    (*)     :: q n ul -> q n ur -> q n (Multiply ul ur)
    negate  :: q n u -> q n u
    abs     :: q n u -> q n u
    signum  :: q n u -> q n u
--    fromInteger :: P.Integer -> q n U

instance (P.Num n, QNum q n, QApplicative q, P.Show (q n U), Eq (q n U)) => P.Num (q n U) where
    (+) = (+) 
    (-) = (-) 
    (*) = (*) 
    abs = abs 
    signum = signum
    fromInteger = qpure . P.fromInteger

(^) :: (QNum q n, QApplicative q) => q n u -> p -> q n (Power u p)
q ^ _ = qpure $ toNum q


subtract :: (QNum q n) => q n u -> q n u -> q n u 
subtract = flip (-)

infixl 7  *
infixl 6  +,-



-- * QFractional - quantitified "Fractional"

class (QNum q n) => QFractional q n where
    (/)    :: q n ul -> q n ur -> q n (Divide ul ur)
    recip  :: q n u -> q n (Divide U u)

infixl 7  /


-- * QReal - quantitified "Real"
class (QNum q n) => QReal q n where
    toRational :: q n u -> P.Rational 

instance (P.Real n, P.Num (q n U), P.Ord (q n U), QReal q n, QApplicative q) => P.Real (q n U) where
    toRational = toRational


-- * Quantitified "Integral"
class  (QReal q n) => QIntegral q n where
    quot          :: q n ul -> q n ur -> q n (Divide ul ur)
    rem           :: q n ul -> q n ur -> q n ul
    div           :: q n ul -> q n ur -> q n (Divide ul ur)
    mod           :: q n ul -> q n ur -> q n ul
    quotRem       :: q n ul -> q n ur -> (q n (Divide ul ur) , q n ul)
    divMod        :: q n ul -> q n ur -> (q n (Divide ul ur) , q n ul)
    toInteger     :: q n u -> P.Integer

instance (P.Integral n, QIntegral q n, QApplicative q, P.Real (q n U), P.Enum (q n U)) => P.Integral (q n U) where
    quot = quot 
    rem = rem
    div = div
    mod = mod
    quotRem = quotRem
    divMod = divMod
    toInteger = toInteger


{-
gcd             :: (Eq (q n u), QIntegral q n) => q n u -> q n u -> q n u
gcd 0 0         =  P.error "Prelude.gcd: gcd 0 0 is undefined"
gcd x y         =  gcd' (abs x) (abs y)
                   where gcd' a 0  =  a
                         gcd' a b  =  gcd' b (a `rem` b)
-}



-- * Quantitified Floating
class (QFractional q n) => QFloating q n where
    pi                  :: q n U
    exp, log            :: q n U -> q n U      
    sqrt                :: (EvenPowers u) => q n u -> q n (SquareRoot u)
    (**), logBase       :: q n U -> q n U -> q n U
    sin, cos, tan       :: q n U -> q n U
    asin, acos, atan    :: q n U -> q n U
    sinh, cosh, tanh    :: q n U -> q n U
    asinh, acosh, atanh :: q n U -> q n U

-- * Quantitified "Applicative" instance
class QApplicative q where
    qpure :: b -> q b u
    (<***>) :: q (c -> d) b -> q c e -> q d g

{-
-- * Numeric functions

even, odd       :: (Eq (q n u), QIntegral q n) => q n u -> Bool
even n          =  n `rem` 2 == 0
odd             =  not . even
-}

{-
(^) :: (QNum a, QIntegral b) => a -> b -> a
x0 ^ y0 | y0 < 0    = error "Negative exponent"
        | y0 == 0   = 1
        | otherwise = f x0 y0
    where -- f : x0 ^ y0 = x ^ y
          f x y | even y    = f (x * x) (y `quot` 2)
                | y == 1    = x
                | otherwise = g (x * x) ((y - 1) `quot` 2) x
          -- g : x0 ^ y0 = (x ^ y) * z
          g x y z | even y = g (x * x) (y `quot` 2) z
                  | y == 1 = x * z
                  | otherwise = g (x * x) ((y - 1) `quot` 2) (x * z)
-}


